// @Author EthanScriptOn
// @Desc
package tool

import (
	"errors"
	"fmt"
	"sync/atomic"
	"unsafe"
)

// ValueHolder The reference object that stores the current lock
type ValueHolder struct {
	value        unsafe.Pointer // Store actuals
	defaultValue unsafe.Pointer // Default value
	state        int32
}

func GenerateValueHolder(defaultValue interface{}) *ValueHolder {
	holder := new(ValueHolder)
	atomic.StorePointer(&holder.defaultValue, unsafe.Pointer(&defaultValue))
	return holder
}

// SetValue Methods are used to set values and are updated only if the old value is the same as the default
func (v *ValueHolder) SetValue(oldValue, newValue interface{}) (bool, interface{}) {
	if !atomic.CompareAndSwapInt32(&v.state, 0, 1) {
		return false, nil
	}
	defer atomic.StoreInt32(&v.state, 0)
	if oldValue == *(*interface{})(atomic.LoadPointer(&v.defaultValue)) {
		atomic.StorePointer(&v.value, unsafe.Pointer(&newValue))
		atomic.StorePointer(&v.defaultValue, unsafe.Pointer(&newValue))
		return true, newValue
	}
	return false, nil
}

func (v *ValueHolder) GetValue() interface{} {
	return *(*interface{})(atomic.LoadPointer(&v.value))
}

// ReentryLock Reentrant locking, the locked logic can be called multiple times in a coroutine,
// and they are mutually exclusive in different coroutines
// This is a simplified version that relies only on queues
type ReentryLock struct {
	holdings           *int32
	defaultValueHolder *ValueHolder
	waitList           *SyncSlice
	isFair             bool
}

// empty Empty object tags
var empty = new(interface{})

type LockUnit struct {
	lockHolder      interface{}
	trough          chan struct{}
	troughSemaphore int32
}

func (l *LockUnit) notice() {
	if atomic.CompareAndSwapInt32(&l.troughSemaphore, 0, 1) {
		l.trough <- struct{}{}
	}
}

func GenerateReentryLock(isFair bool) *ReentryLock {
	var holdings int32 = 0
	return &ReentryLock{
		holdings:           &holdings,
		defaultValueHolder: GenerateValueHolder(empty),
		waitList:           GenerateSyncSlice(),
		isFair:             isFair,
	}
}

// Lock locked
func (r *ReentryLock) Lock(lockHolder interface{}) {
	fmt.Println(fmt.Sprintf("%v 开始上锁", lockHolder))
	if lockHolder == nil {
		panic("lockHolder is nil")
	}
	// Fair locks
	if r.isFair {
		if !r.hasBlock() && atomic.CompareAndSwapInt32(r.holdings, 0, 1) {
			if swapped, _ := r.swap(empty, lockHolder); swapped {
				fmt.Println(fmt.Sprintf("上锁成功！！！，ID是：%v", lockHolder))
				return
			}
			fmt.Println(fmt.Sprintf("上锁失败重新上锁，ID是：%v", lockHolder))
			atomic.StoreInt32(r.holdings, -1)
			r.Lock(lockHolder)
		} else if currHolder := r.load(); currHolder == lockHolder {
			// The same coroutine does not need to be locked repeatedly
			atomic.AddInt32(r.holdings, 1)
			return
		}
		if !r.fairTryLock(lockHolder) {
			fmt.Println(fmt.Sprintf("二次上锁失败准备阻塞，ID是：%v", lockHolder))
			r.block(lockHolder)
		}
	} else {
		// Unfairly lock luck psychology attempts to add locks
		if atomic.CompareAndSwapInt32(r.holdings, 0, 1) {
			if swapped, _ := r.swap(empty, lockHolder); swapped {
				return
			}
			r.Lock(lockHolder)
		} else if currHolder := r.load(); currHolder == lockHolder {
			// The same coroutine does not need to be locked repeatedly
			atomic.AddInt32(r.holdings, 1)
			return
		}
		if !r.unFairTryLock(lockHolder) {
			r.block(lockHolder)
		}
	}
}

func (r *ReentryLock) fairTryLock(lockHolder interface{}) bool {
	if atomic.LoadInt32(r.holdings) == 0 {
		if !r.hasBlock() {
			if swapped, _ := r.swap(empty, lockHolder); swapped {
				atomic.AddInt32(r.holdings, 1)
				fmt.Println(fmt.Sprintf("%v ，给holdings加了一，此时的holdings是 %v", lockHolder, *r.holdings))
				return true
			} else {
				fmt.Println(fmt.Sprintf("%v ，fairTryLock 失败了", lockHolder))
				return false
			}
		} else {
			fmt.Println(fmt.Sprintf("%v ，hasBlock不为空", lockHolder))
			return false
		}
	} else {
		fmt.Println(fmt.Sprintf("%v 查看holdings，holdings数量为：%v", lockHolder, *r.holdings))
		return false
	}
	//if atomic.LoadInt32(r.holdings) == 0 && !r.hasBlock() {
	//	if swapped, _ := r.swap(empty, lockHolder); swapped {
	//		atomic.AddInt32(r.holdings, 1)
	//	}
	//}
}

func (r *ReentryLock) unFairTryLock(lockHolder interface{}) bool {
	if atomic.LoadInt32(r.holdings) == 0 {
		if swapped, _ := r.swap(empty, lockHolder); swapped {
			atomic.AddInt32(r.holdings, 1)
		}
	}
	return false
}

func (r *ReentryLock) hasBlock() bool {
	block, err := r.waitList.GetFirst()
	if err != nil {
		panic(err)
	}
	return block != nil
}

// Enter the blocking queue and wait to be woken up
func (r *ReentryLock) block(lockHolder interface{}) {
	// Build a blocked node
	lockUnit := &LockUnit{
		lockHolder: lockHolder,
		trough:     make(chan struct{}),
	}
	// Try locking
	if r.isFair {
		if r.fairTryLock(lockHolder) {
			return
		}
	} else {
		if r.unFairTryLock(lockHolder) {
			return
		}
	}
	// Enter the blocking queue
	if err := r.waitList.Add(lockUnit); err != nil {
		panic(err)
	}
	// You need to try to lock before blocking,Prevent null wake-up (Try locking)
	if r.isFair {
		if r.fairTryLock(lockHolder) {
			fmt.Println(fmt.Sprintf("沉睡前尝试加锁成功: %v", lockUnit.lockHolder))
			// If the lock fails, the waiter needs to be removed
			if err := r.waitList.Remove(func(index int, item interface{}) bool {
				if item == lockUnit {
					fmt.Println(fmt.Sprintf("沉睡前尝试加锁成功了进行移除操作: %v", lockUnit.lockHolder))
				}
				// Move out of the awakened
				return item == lockUnit
			}); err != nil {
				panic(err)
			}
			return
		}
	} else {
		if r.unFairTryLock(lockHolder) {
			// If the lock fails, the waiter needs to be removed
			if err := r.waitList.Remove(func(index int, item interface{}) bool {
				// Move out of the awakened
				return item == lockUnit
			}); err != nil {
				panic(err)
			}
			return
		}
	}
	fmt.Println(fmt.Sprintf("已经沉睡了: %v, 此时slice中的元素有：%v", lockHolder, r.waitList.Print()))
	// blocking
	_, ok := <-lockUnit.trough
	fmt.Println(fmt.Sprintf("被唤醒了拿到的是: %v, %v", lockUnit.lockHolder, ok))
	// The awakened coroutine needs to reacquire the lock
	r.Lock(lockHolder)
}

// unBlock Contact blockage
func (r *ReentryLock) unBlock(lockHolder interface{}) error {
	state := atomic.LoadInt32(r.holdings) - 1
	canRelease := state == 0

	if canRelease {
		// Get out as soon as possible
		if swapped, _ := r.swap(lockHolder, empty); !swapped {
			fmt.Println("重新解锁")
			return r.unBlock(lockHolder)
		}
	}
	// holdings is a race operation to be put at the back
	atomic.AddInt32(r.holdings, -1)

	if !canRelease {
		fmt.Println(fmt.Sprintf("ID为: %v 的还不能唤醒其他等待者，此时的holdings为 %v", lockHolder, state+1))
		return nil
	}
	// The lock has been lifted
	fmt.Println(fmt.Sprintf("ID为: %v 的解锁成功", lockHolder))
	// Gets the waiting goroutine from the queue, even if there are
	// other gorouting to get the lock at that time
	var target *LockUnit
	fmt.Println(fmt.Sprintf("%v 要开始唤醒一个等待者了，此时的等待者列表是：%v", lockHolder, r.waitList.Print()))
	// Traversal requires a mutex to prevent multiple goroutines from waking up the same waiter here
	err := r.waitList.ForEachWithMutex(func(item interface{}) (bool, error) {
		if item != nil {
			fmt.Println(fmt.Sprintf("循环打印等待者: %v", item))
			lu, ok := item.(*LockUnit)
			if !ok {
				return true, errors.New("wrong element type in the waiting list, it should be *LockUnit")
			}
			target = lu
			return true, nil
		}
		return false, nil
	})
	if err != nil {
		return err
	}
	if target != nil {
		if err = r.waitList.Remove(func(index int, item interface{}) bool {
			// Move out of the awakened
			if item == target {
				fmt.Println(fmt.Sprintf("%v 删除了一个等待者 %v", lockHolder, target.lockHolder))
				return true
			}
			return false
		}); err != nil {
			return err
		}
		fmt.Println(fmt.Sprintf("%v 删除了一个等待者 %v，此时的等待者列表是：%v", lockHolder, target.lockHolder, r.waitList.Print()))
		// The awakened ones begin to snatch the lock resources
		fmt.Println(fmt.Sprintf("开始唤醒沉睡的协程 当前的是: %v 拿到的等待者是: %v", lockHolder, target.lockHolder))
		// There must be an opportunity to wake up and only once
		target.notice()
	}
	return nil
}

// UnLock unlock
func (r *ReentryLock) UnLock(lockHolder interface{}) {
	fmt.Println("开始解锁")
	if lockHolder == nil {
		panic("lockHolder is nil")
	}
	if r.load() != lockHolder {
		panic("unlock exception, not the holder of the lock cannot unlock it")
	}
	if err := r.unBlock(lockHolder); err != nil {
		panic(err)
	}
}

// swap Sets the reference holder of the current coroutine
func (r *ReentryLock) swap(old, new interface{}) (bool, interface{}) {
	return r.defaultValueHolder.SetValue(old, new)
}

// load gets the lock pointer
func (r *ReentryLock) load() interface{} {
	return r.defaultValueHolder.GetValue()
}
